OpenCL 创建上下文

您所在的位置:网站首页 context count OpenCL 创建上下文

OpenCL 创建上下文

2024-07-03 23:57| 来源: 网络整理| 查看: 265

上下文为关联的设备、内存对象、命令队列、程序对象、内核对象提供一个容器。上下文是OpenCL应用的核心。正是上下文驱动着应用程序与特定设备以及特定设备之间的通信。

对于上下文中关联的所有计算设备必须全都来自于同一平台,对于来自不同平台的OpenCL设备,需要为各个平台独立地创建上下文。对于同一平台的设备,上下文中可以关联多个设备。主机应用也可以使用多个上下文来管理多个设备,甚至同一个平台多个设备都可以关联到不同的上下文,如下图所示。

平台、设备和上下文

在上图中,平台1有多个CPU和GPU设备。同一平台的设备可以关联到同一上下文中,所以上下文1、上下文2中关联CPU和GPU设备是可行的。

对于平台中的设备,上下文不是非要关联所有的设备,所以在上下文4中只是关联了部分设备。而不同平台的设备不能关联到同一上下文中,所以关联平台1中的GPU3和平台2中的CPU1的上下文3是不合法的。

OpenCL 创建上下文

OpenCL上下文对象用cl_context类型表示,可以使用如下两个函数其中之一来创建上下文:

cl_context clCreateContext(const cl_context_properties *properties , cl_uint num_devices , const cl_device_id *devices , void (CL_CALLBACK *pfn_notify ) (const char *, const void *, size_t, void *), void *user_data , cl_int *errcode_ret ) cl_context clCreateContextFromType(const cl_context_properties *properties, cl_device_type device_type , void (CL_CALLBACK *pfn_notify ) (const char *, const void *, size_t, void *), void *user_data, cl_int *errcode_ret)

两个函数用法很类似,主要的区别在于:

clCreateContext显示地指定设备(devices)来创建上下文;

clCreateContextFromType根据给定的设备类型(device_type)来创建上下文。对设备类型详见下表。

参数properties指定上下文属性名称及属性相应的值的列表,且最后一个元素为0,见下表。当实现自定义选择平台时,参数properties可以设置为NULL。

clCreateContext支持的属性

参数pfn_notify和user_data用来共同定义一个回调函数,可以调用这个回调报告上下文生命周期中出现错误的有关信息,要把user_data作为最后一个参数传至回调函数。user_data为void类型指针,也就是可以指向任何数据,当错误发生时user_data能够提供信息。参数pfn_notf iy和user_data也都可以设置为NULL。参数errcode_ret为函数的返回状态,成功执行值为CL_SUCCESS,否则值为对应的错误代码。

下面的代码清单展示了给定一个平台,查询平台中GPU设备,并创建上下文:

cl_device_id *device; cl_platform_id platform; cl_int err; cl_uint NumDevice; //选择第一个平台 err = clGetPlatformIDs(1, &platform, NULL); err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &NumDevice); device = (cl_device_id *)malloc(sizeof(cl_device_id) * NumDevice); //选择GPU设备 err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, NumDevice, device, NULL); //创建上下文 cl_context_properties properites[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0}; //指定设备创建上下文 //cl_context context=clCreateContext(properites,NumDevice,device,NULL,NULL,&err); //指定设备类型创建上下文 cl_context context = clCreateContextFromType(properites, CL_DEVICE_TYPE_GPU, NULL, NULL, &err);

例子中,clCreateContext函数把平台中查询到的所有GPU设备都关联到创建的上下文中。clCreateContextFromType则是选择第一平台中的GPU设备。从功能来说,两个函数实现的功能是一样的。

clGetContextInfo

给定上下文,可以使用如下函数查询上下文各个属性信息:

cl_int clGetContextInfo(cl_context context , cl_context_info param_name , size_t param_value_size , void *param_value , size_t *param_value_size_ret )

参数param_name为查询属性名称,取值见下表,参数param_value返回上下文查询属性信息。

上下文属性查询

下面的代码清单展示了使用clContextInfo()查询上下文关联的设备数目及上下文的引用计数方法:

cl_platform_id platform; cl_int err; cl_uint NumDevice; err = clGetPlatformIDs(1, &platform, NULL); cl_context_properties properites[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 }; cl_context context = clCreateContextFromType(properites, CL_DEVICE_TYPE_ALL, NULL, NULL, &err); NumDevice = 0; size_t DeviceSize; err = clGetContextInfo(context, CL_CONTEXT_NUM_DEVICES, sizeof(cl_uint), &NumDevice, NULL); printf("Number of Device in context:%d\n", NumDevice);

上述代码片段中,在调用函数clContextInfo时,对其参数param_name指定的是CL_CONTEXT_NUM_DEVICES,从字面上理解非常直观,即我们要查询当前上下所包含的计算设备个数。除了可指定CL_CONTEXT_NUM_DEVICES这个属性以外,还能指定CL_CONTEXT_REFERENCE_COUNT这一属性,表示查询当前上下文的引用计数值。在之前我们没有讲解任何关于OpenCL中引用计数的概念,那这个引用计数到底是什么?它又有何作用呢?

在使用clCreateContext或clCreateContextFromType创建上下文时,并不像我们之前创建平台和设备时返回错误代码,而是直接返回cl_context对象,此时该上下文对象的引用计数为1。

其实,OpenCL对很多对象采用了类似于Apple的Cocoa Framework中的内存管理机制(毕竟OpenCL的初稿出自Apple)。在这种机制下,主张谁分配了某个对象,那么谁就负责释放该对象。如果这个对象不是由你来分配的,那么你也不用去释放它。我们通过OpenCL接口就能看出哪些对象是被分配的,哪些不是。例如,clGetPlatformIDs函数所获得的platform_id对象就不需要通过某个接口进行释放,因为它是通过Get获得的。

类似的是, clGetDeviceIDs也同样如此。这里的clCreateContext我们看到用的是Create这个前缀,当我们看到这个前缀时就要想到一定有一个与之相对应的Retain接口和Release接口。Retain接口用于显式地做引用计数加1操作;而Release则是显式地做引用计数减1操作,而只有当引用计数为0时,Release操作才会释放对应的空间。而引入这个引用计数机制对于第三方库或者跨模块的开发非常有利。例如,我们在模块A创建了一个上下文,然后在做完某个计算后把它提交给模块B继续计算。

当然,出于性能要求,模块B借用了这个上下文对象之后可能会自己另建一个命令队列然后执行,因此与模块A的后续操作完全可以是异步的。这就会引发一个问题,当模块A执行完之后,把该上下文对象释放掉,而模块B此时还在使用这个上下文对象。因此,如果没有引用计数,而是直接把此上下文对象释放掉,那么模块B在使用此对象时就可能会引发异常。而有了引用计数机制,我们可以遵循这个机制来做——在模块A中创建上下文对象,那么在A用完之后通过Release接口将它释放。如果要把此对象交给模块B做后续操作,那么要由模块B对此对象做一次Retain操作,然后模块B操作完成之后也要调用一次Release操作来释放此上下文对象。这样一来,上下文对象就能安全而又完整地被模块A与模块B共同使用了。

上下文对象的Retain和Release接口

对于上下文对象的Retain和Release接口如下:

cl_int clRetainContext(cl_context context ) cl_int clReleaseContext(cl_context context )

clRetainContext增加引用计数(引用计数+1),clReleaseContext减少引用计数(引用计数-1)。如果外部函数访问预先创建的上下文,确保在处理前调用clRetainContext,处理完成后调用clReleaseContext。如果在创建上下文的函数中,在函数完成前调用clReleaseContext来减少引用计数,使其值为0,释放上下文空间。

下面的代码展示了这两个函数的使用方式:

… cl_context_properties properites[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 }; cl_context context = clCreateContextFromType(properites, CL_DEVICE_TYPE_ALL, NULL, NULL, NULL); cl_uint ReferenCount; clGetContextInfo(context, CL_CONTEXT_REFERENCE_COUNT, sizeof(cl_uint), &ReferenCount, NULL); printf("Initial Reference Count: %d\n ", ReferenCount); clRetainContext(context); clGetContextInfo(context, CL_CONTEXT_REFERENCE_COUNT, sizeof(cl_uint), &ReferenCount, NULL); printf("Reference Count: %d\n ", ReferenCount); clReleaseContext(context); clGetContextInfo(context, CL_CONTEXT_REFERENCE_COUNT, sizeof(cl_uint), &ReferenCount, NULL); printf("Reference Count: %d\n ", ReferenCount);

如上代码,输出为:

Initial Reference Count:1 Reference Count:2 Reference Count:1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3